/**
 * 
 */
package cn.zhucongqi.services;

import javax.servlet.http.HttpServletRequest;

import cn.zhucongqi.consts.Consts;
import cn.zhucongqi.exception.AuthProblemException;
import cn.zhucongqi.issuer.AuthIssuerKit;
import cn.zhucongqi.kit.AuthExceptionHandleKit;
import cn.zhucongqi.response.AccessToken;
import cn.zhucongqi.response.CodeResponse;
import cn.zhucongqi.response.ErrorResponse;
import cn.zhucongqi.response.ResponseKit;
import cn.zhucongqi.validators.AccessTokenValidator;
import cn.zhucongqi.validators.AuthorizationValidator;
import cn.zhucongqi.validators.ClientCredentials;
import cn.zhucongqi.validators.RefreshTokenValidator;

import com.jfinal.ext2.core.Service;
import com.jfinal.kit.StrKit;

/**
 * @author BruceZCQ [zcq@zhucongqi.cn]
 * @version
 */
public class AuthService extends Service implements ClientCredentials<HttpServletRequest> {
	
	private void repErrorToClient(HttpServletRequest request, AuthProblemException e) {
		// rep error to client
		ErrorResponse errorRep = ResponseKit.errorRep(request, e);
		this.controller.getResponse().setStatus(e.getResponseStatus());
		this.controller.renderJson(errorRep.param());
	}

	/**
	 * Authorization  Code Request <br/>
	 * response_type : code <br/>
	 * client_id : The client identifier; <br/>
	 * scope : The scope of the access request; <br/>
	 * state : An opaque value used by the client to maintain
         state between the request and callback.The authorization
         server includes this value when redirecting the user-agent back
         to the client. <br/>
     * Success => <br/>
     * code : The authorization code generated by the
         authorization server.  The authorization code MUST expire
         shortly after it is issued to mitigate the risk of leaks.  A
         maximum authorization code lifetime of 10 minutes is
         RECOMMENDED.
     * state : The exact value received from the
         client.
     * Failure => <br/>
     * error : ErrorConsts.TokenResponse <br/>
     * error_description : the erros repsonse description <br/>
     * state : The exact value received from the
         client.
	 */
	public void authrize() {
		HttpServletRequest request = this.controller.getRequest();
		AuthorizationValidator validator = new AuthorizationValidator();
		try {
			// validate request
			validator.validate(request);
			// make code
			CodeResponse codeRep = ResponseKit.codeRep(request);
			AuthIssuerKit issuer = AuthIssuerKit.md5Issuer();
			codeRep.setCode(issuer.authorizationCode());
			// rep to client
			this.controller.renderJson(codeRep.param());
		} catch (AuthProblemException e) {
			this.repErrorToClient(request, e);
		}
	}
	
	/**
	 * Access Token Request <br/>
	 * 
	 * response_type : token <br/>
	 * grant_type : accessToken <br/>
	 * code : The authorization code received from the
         authorization server. <br/>
     * client_id : The client identifier; <br/>
     * username : The resource owner username. <br/>
     * password : The resource owner password. <br/>
	 * scope : The scope of the access request; <br/>
     * state : An opaque value used by the client to maintain
         state between the request and callback.The authorization
         server includes this value when redirecting the user-agent back
         to the client. <br/>
     * Success => <br/> 
     * access_token : The access token issued by the authorization server. <br/>
     * expires_in : The lifetime in seconds of the access token. <br/>
     * refresh_token : he refresh token, which can be used to obtain new
         access tokens using the same authorization grant. <br/>
     * scope : The scope of the access request; <br/>
     * state : The exact value received from the
         client. <br/>
    For example:

     HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache

     {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
       "scope":"user",
       "state":"xyz"
     }
     * Failure => <br/>
     * error : ErrorConsts.TokenResponse <br/>
     * error_description : the erros repsonse description <br/>
     * state : The exact value received from the
         client.
	 */
	public void accessToken() {
		HttpServletRequest request = this.controller.getRequest();
		AccessTokenValidator validator = new AccessTokenValidator();
		validator.setCustomClientCredentialsValidator(this);
		try {
			validator.validate(request);
			AccessToken accessToken = ResponseKit.tokenRep(request);
			AuthIssuerKit issuer = AuthIssuerKit.md5Issuer();
			accessToken.setAccessToken(issuer.accessToken())
			.setExpiresIn("3600").setRefreshToken(issuer.refreshToken());
			this.controller.renderJson(accessToken.param());
		} catch (AuthProblemException e) {
			this.repErrorToClient(request, e);
		}
	}
	
	/**
	 * Refreshing an Access Token <br/>
	 * 	 
	 * response_type : token <br/>
	 * grant_type : refresh_token <br/>
	 * refresh_token : The refresh token issued to the client. <br/>
     * client_id : The client identifier; <br/>
     * client_secret : The client secret. <br/>
     * username : The resource owner username. <br/>
     * password : The resource owner password. <br/>
	 * scope : The scope of the access request; <br/>    
	 * state : An opaque value used by the client to maintain
         state between the request and callback.The authorization
         server includes this value when redirecting the user-agent back
         to the client. <br/>
	 * Success => <br/>
	 * access_token : The access token issued by the authorization server. <br/>
     * expires_in : The lifetime in seconds of the access token. <br/>
     * refresh_token : he refresh token, which can be used to obtain new
         access tokens using the same authorization grant. <br/>
     * scope : The scope of the access request; <br/>
     * state : The exact value received from the
         client. <br/>
	 * Failure => <br/>
     * error : ErrorConsts.CodeResponse <br/>
     * error_description : the erros repsonse description <br/>
	 * scope : The scope of the access request; <br/>  
     * state : The exact value received from the
         client.
	 */
	public void refreshToken() {
		HttpServletRequest request = this.controller.getRequest();
		RefreshTokenValidator validator = new RefreshTokenValidator();
		validator.setCustomClientCredentialsValidator(this);
		try {
			validator.validate(request);
			AccessToken accessToken = ResponseKit.tokenRep(request);
			AuthIssuerKit issuer = AuthIssuerKit.md5Issuer();
			accessToken.setAccessToken(issuer.accessToken())
			.setExpiresIn("3600").setRefreshToken(issuer.refreshToken());
			this.controller.renderJson(accessToken.param());
		} catch (AuthProblemException e) {
			this.repErrorToClient(request, e);
		}
	}

	@Override
	public void validateClientCredentials(HttpServletRequest request) throws AuthProblemException {
		String client_id = request.getParameter(Consts.AuthConsts.AUTH_CLIENT_ID);
		
		String username = request.getParameter(Consts.AuthConsts.AUTH_USERNAME);
		if (StrKit.isBlank(username) || !client_id.equals(username)) {
			throw AuthExceptionHandleKit.handleInvalidClientException();
		}

		String client_secret = request.getParameter(Consts.AuthConsts.AUTH_CLIENT_SECRET);
		String password = request.getParameter(Consts.AuthConsts.AUTH_PASSWORD);
		if (StrKit.isBlank(password) || !client_secret.equals(password)) {
			throw AuthExceptionHandleKit.handleInvalidClientException();
		}		
	}
}
